﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace newcj_routerperformancemonitor.Core
{
    public static class Monitor
    {
        private const int DefaultTerminalsExpiredTime = 5;

        /// <summary>
        /// Update terminals interval. In Minutes, default is 5.
        /// </summary>
        public static int TerminalsExpiredTime { get; set; }

        /// <summary>
        /// IPv4 gateway. It's your router ip.
        /// </summary>
        public static string Gateway { get; set; }

        private static Ping _ping;
        private static Timer _terminalsUpdateTimer;

        private static Terminal[] _terminals;

        private static IList<MonitorLog> _logs;

        public static event EventHandler<LoggedEventArgs> Logged;

        static Monitor()
        {
            TerminalsExpiredTime = DefaultTerminalsExpiredTime;
        }

        public static async void Start()
        {
            var gateway = Gateway;
            if (string.IsNullOrEmpty(gateway)) throw new ArgumentException("Please set Gateway.");

            IPAddress ipAddress = null;
            IPAddress.TryParse(gateway, out ipAddress);
            if (ipAddress == null) throw new ArgumentException("Please set Ipv4 Gateway.");

            if (_logs == null) _logs = new List<MonitorLog>();

            if (_ping == null)
            {
                _ping = new Ping();
                _ping.CallBack += PingCallBack;
            }

            await UpdateTerminals();

            _ping.Ip = gateway;
            _ping.Start();

            if (_terminalsUpdateTimer != null) _terminalsUpdateTimer.Dispose();
            _terminalsUpdateTimer = new Timer(TerminalsExpired, null, TerminalsExpiredTime * 60000, TerminalsExpiredTime * 60000);
        }

        public static IList<MonitorLog> Stop()
        {
            if (_ping != null) _ping.Stop();
            if (_terminalsUpdateTimer != null)
            {
                _terminalsUpdateTimer.Dispose();
                _terminalsUpdateTimer = null;
            }

            return _logs;
        }

        public static void Clear()
        {
            TerminalsExpiredTime = DefaultTerminalsExpiredTime;
            _terminals = null;
            if (_logs != null) _logs.Clear();
        }

        private static void OnLog(MonitorLog log)
        {
            var logged = Logged;
            if (logged != null)
            {
                logged(null, new LoggedEventArgs(log));
            }
        }

        private static async Task UpdateTerminals()
        {
            var terminals = await TerminalManager.Find(Gateway);
            _terminals = terminals;
        }

        private static async void TerminalsExpired(object obj)
        {
            await UpdateTerminals();
        }

        private static void PingCallBack(object sender, PingEventArgs e)
        {
            if (_logs != null)
            {
                _logs.Add(new MonitorLog(e.Timeout, _terminals));
            }
            OnLog(new MonitorLog(e.Timeout, _terminals));
        }

    }

    public class MonitorLog
    {
        public int GatewayTimeout { get; private set; }
        public Terminal[] Terminals { get; private set; }

        internal MonitorLog(int getewayTimeout, Terminal[] terminals)
        {
            GatewayTimeout = getewayTimeout;
            Terminals = terminals;
        }

        public override string ToString()
        {
            return string.Format("GatewayTimeout={0},TerminalsCount={1}", GatewayTimeout, Terminals.Length);
        }
    }

    public class LoggedEventArgs : EventArgs
    {
        public MonitorLog Log { get; private set; }

        internal LoggedEventArgs(MonitorLog log)
        {
            Log = log;
        }
    }
}
